
#include <BOpenGL>
#include <BRender>
#include <BMesh>
#include "member_BMesh.h"

using namespace BWE;

#define member					(*(member_BMesh*)_ptr)
#define member_allocate()		_ptr = new member_BMesh(this)
#define member_release()		delete (member_BMesh*)_ptr

BMesh::BMesh(const BString& name)
{
	member_allocate();
	this->setName(name);
}
BMesh::~BMesh()
{
	member_release();
}

bool BMesh::setTexCoords(BCoordArray* coords, int unit)
{
	if (unit >= 0 && unit < 8)
	{
		member.coords[unit] = coords;
		return true;
	}
	this->dirty();
	return false;
}
const BCoordArray* BMesh::texCoords(int unit) const
{
	return member.coords[unit];
}
BCoordArray* BMesh::texCoords(int unit)
{
	return member.coords[unit];
}

void BMesh::setFaces(BFaceArray* faces)
{
	if (member.faces != faces)
	{
		member.faces = faces;
	}
	this->dirty();
}
BFaceArray* BMesh::faces()
{
	return member.faces.ptr();
}
const BFaceArray* BMesh::faces() const
{
	return member.faces.ptr();
}

void BMesh::setVertices(BVectorArray* vertices)
{
	if (member.vertices != vertices)
	{
		member.vertices = vertices;
	}
	this->dirty();
}
BVectorArray* BMesh::vertices()
{
	return member.vertices.ptr();
}
const BVectorArray* BMesh::vertices() const
{
	return member.vertices.ptr();
}

void BMesh::setNormals(BVectorArray* normals)
{
	if (member.normals != normals)
	{
		member.normals = normals;
	}
	this->dirty();
}
BVectorArray* BMesh::normals()
{
	return member.normals.ptr();
}
const BVectorArray* BMesh::normals() const
{
	return member.normals;
}

void BMesh::setColors(BColorArray* colors)
{
	if (member.colors != colors)
	{
		member.colors = colors;
	}
	this->dirty();
}
BColorArray* BMesh::colors()
{
	return member.colors;
}
const BColorArray* BMesh::colors() const
{
	return member.colors;
}

void BMesh::clear()
{
	member.vertices = 0;
	member.normals = 0;
	member.faces = 0;
	member.colors = 0;
	member.coords[0] = 0;
	member.coords[1] = 0;
	member.coords[2] = 0;
	member.coords[3] = 0;
	member.coords[4] = 0;
	member.coords[5] = 0;
	member.coords[6] = 0;
	member.coords[7] = 0;
	this->dirty();
}
bool BMesh::empty() const
{
	return member.vertices == 0;
}
void BMesh::smooth()
{
	BVectorArray* vertices = member.vertices;
	BFaceArray* faces = member.faces;
	if (!vertices || !faces)
		return;
	BVectorArray* normals = member.normals;
	if (!normals || normals->size() != vertices->size())
		normals = new BVectorArray(vertices->size());
	normals->fill(BVector());
	for (int i = 0; i < faces->size(); i++)
	{
		BFace& face = (*faces)[i];
		const BVector& va = (*vertices)[face.a()];
		const BVector& vb = (*vertices)[face.b()];
		const BVector& vc = (*vertices)[face.c()];
		BVector normal = (vb - va).cross(vc - vb);
		if (normal.normalize())
		{
			(*normals)[face.a()] += normal;
			(*normals)[face.b()] += normal;
			(*normals)[face.c()] += normal;
		}
	}
	for (int i = 0; i < normals->size(); i++)
	{
		(*normals)[i].normalize();
	}
	this->setNormals(normals);
}
void BMesh::reversal()
{
	BFaceArray* faces = member.faces;
	if (!faces)
		return;
	for (int i = 0; i < faces->size(); i++)
	{
		BFace& face = (*faces)[i];
		unsigned short a = face.a();
		face.a() = (face.c());
		face.c() = (a);
	}
	BVectorArray* normals = member.normals;
	if (!normals)
		return;
	for (int i = 0; i < normals->size(); i++)
	{
		(*normals)[i] = -(*normals)[i];
	}
	this->dirty();
}
void BMesh::optimize()
{

}

BBox BMesh::refresh()
{
	BSpace space;
	if (member.vertices && member.vertices->size())
	{
		space.reset(member.vertices->first());
		for (int i = 0; i < member.vertices->size(); i++)
		{
			space.expand((*member.vertices.ptr())[i]);
		}
	}
	return space;
}
void BMesh::render(BRender& render)
{
	if(!member.vertices || member.vertices->empty())
		return;

	if (member.vertices)
	{
		glEnableClientState(GL_VERTEX_ARRAY);
		glVertexPointer(3, GL_FLOAT, 0, member.vertices->data());
	}

	if (member.normals)
	{
		glEnableClientState(GL_NORMAL_ARRAY);
		glNormalPointer(GL_FLOAT, 0, member.normals->data());
	}

	if (member.colors)
	{
		glEnableClientState(GL_COLOR_ARRAY);
		glColorPointer(4, GL_UNSIGNED_BYTE, 0, member.colors->data());
	}

	if (member.coords[0] && this->texture(0))
	{
		glEnableClientState(GL_TEXTURE_COORD_ARRAY);
		glTexCoordPointer(2, GL_FLOAT, 0, member.coords[0]->data());
	}

	if (member.faces)
	{
		glEnableClientState(GL_INDEX_ARRAY);
		glDrawElements(GL_TRIANGLES, member.faces->size() * 3, GL_UNSIGNED_INT, member.faces->data());
		glDisableClientState(GL_INDEX_ARRAY);
	}
	else
	{
		glDrawArrays(GL_TRIANGLES, 0, member.vertices->size());
	}

	if (member.vertices)
	{
		glDisableClientState(GL_VERTEX_ARRAY);
	}
	if (member.normals)
	{
		glDisableClientState(GL_NORMAL_ARRAY);
	}
	if (member.colors)
	{
		glDisableClientState(GL_COLOR_ARRAY);
	}
	if (member.coords[0] && this->texture(0))
	{
		glDisableClientState(GL_TEXTURE_COORD_ARRAY);
	}
}
